﻿package com.frontalcode 
{
	import flash.events.*;
	import flash.utils.*;

    public class ExternalAssetManager extends OptionableObject
    {
		internal var document : Document;
		
		// Our external assets.
		//
		private var assets : Array = [ ];
		
		// The listeners object tracks those calls to getDefinition that failed 
		// but that want an onLoad callback when the definition is loaded.
		//
		// The key of the object is the name of interest. Each value is an Array
		// of objects with keys externalAssetSrc and onLoad.
		//
		private var listeners : Object = { };
		
		public function ExternalAssetManager ( document : Document, options : Object = null )
		{
			super ( options );
			
			this.document = document;
		}
		
		internal function load ( url : String, baseURL : String = null, checkPolicyFile : Boolean = false, policyFileURL : String = null, loadInCurrentAppAndSecDomains : Boolean = false ) : Asset
		{
			var asset : Asset = new Asset ( document, url, baseURL, checkPolicyFile, policyFileURL, loadInCurrentAppAndSecDomains );
			AssetManager.getInstance ( ).preloadObject ( asset );
			assets.push ( asset );
			addListener ( asset, FrontalEvent.LOADED, assetLoadHandler );
			return asset;
		}
		
		/*
			Function: assetLoadHandler
			
				We wait for the external assets to load and as they do we loop
				through our listeners to see if any of their symbols have been
				resolved. If so, then we call the onLoad closure and remove the
				listener.
		*/
		internal function assetLoadHandler ( event : Event ) : void
		{
			for ( var name : String in listeners )
			{
				try
				{
					var classRef : Class = ( event.target as Asset ).loader.contentLoaderInfo.applicationDomain.getDefinition ( name ) as Class;
					
					if ( listeners [ name ] != null )
					{
						for ( var i : int = 0; i < listeners [ name ].length; i++ )
						{
							var listener : Object = listeners [ name ] [ i ];
							
							if ( listener.externalAssetSrc == null || Util.isEmpty.test ( listener.externalAssetSrc ) || listener.externalAssetSrc == ( event.target as Asset ).rawURL )
							{
								listeners [ name ].splice ( i--, 1 );
								listener.onLoad ( name, listener.externalAssetSrc, classRef );
							}
						}
						
						if ( listeners [ name ].length == 0 ) delete listeners [ name ];
					}
				}
				catch ( e : Error )
				{
				}
			}
		}
		
		/*
			Function: getDefinition
			
				This function will search the current application domain and all 
				of the loaded application domains for the given name. If 
				externalAssetSrc is specified, then it will only search the
				application domain of the associated external asset SWF.
				
				If the definition is not found and if onLoad if specified, then 
				as an external asset is loaded, it will be checked for the given
				name and if it is found, onLoad will be called. Use 
				<ignoreDefinition> to remove this callback at any time.
				
			Parameters:
			
				name				- The definition being sought. This may be
									  of a static function or a class.
				onLoad				- A closure to be called if the name is not 
									  found immediately. optional.
									  
									  internal function onLoad ( name : String, externalAssetSrc : String, classRef : Class ) : void
									  
				externalAssetSrc	- The url of the external swf to seek the
									  name in. By default, all external assets
									  will be examined.
		*/
		public function getDefinition ( name : String, onLoad : Function = null, externalAssetSrc : String = null ) : *
		{
			var result : *;
			
			var className : String = name.replace ( /^(.*)\.[^.]+$/, "$1" );
			var fnName : String;

			if ( className.length < name.length ) fnName = name.replace ( /^.*\.([^.]+)$/, "$1" );

			if ( externalAssetSrc == null || Util.isEmpty.test ( externalAssetSrc ) )
			{
				// First try it as a class or package level function.
				//
				try
				{
					result = getDefinitionByName ( name );
				}
				catch ( e : Error )
				{
				}
				
				// Then try as a static function.
				//
				if ( result == null && fnName != null )
				{
					try
					{
						var classRef : Class = getDefinitionByName ( className ) as Class;
						result = classRef [ fnName ];
						
					}
					catch ( e : Error )
					{
					}
				}
			}
			
			if ( result == null )
			{
				for each ( var extAsset : Asset in assets )
				{
					if ( externalAssetSrc == null || Util.isEmpty.test ( externalAssetSrc ) || extAsset.rawURL == externalAssetSrc )
					{
						// First try it as a class.
						//
						try
						{
							result = extAsset.loader.contentLoaderInfo.applicationDomain.getDefinition ( name ) as Class;
							break;
						}
						catch ( e : Error )
						{
						}
					}
				
					// Then try as a static function.
					//
					if ( result == null && fnName != null )
					{
						try
						{
							classRef = extAsset.loader.contentLoaderInfo.applicationDomain.getDefinition ( className ) as Class;
							result = classRef [ fnName ];
						}
						catch ( e : Error )
						{
						}
					}
				}
			}
			
			if ( result == null && onLoad != null )
			{
				if ( listeners [ name ] == null ) listeners [ name ] = [ ];
				
				if ( findListener ( name, onLoad, externalAssetSrc ) < 0 )
				{
					listeners [ name ].push ( { onLoad: onLoad, externalAssetSrc: externalAssetSrc } );
				}
			}
			
			return result;
		}
		
		/*
			Function: ignoreDefinition
			
				Use this function to remove an onLoad callback added by calling
				getDefinition.
				
			Parameters:
			
				name				- The definition being sought
				onLoad				- The callback closure.
				externalAssetSrc	- The url of the external swf to seek the
									  name in. Optional.
		*/
		public function ignoreDefinition ( name : String, onLoad : Function, externalAssetSrc : String = null ) : void
		{
			var index : int = findListener ( name, onLoad, externalAssetSrc );
			if ( index >= 0 ) listeners [ name ].splice ( index, 1 );
		}
		
		private function findListener ( name : String, onLoad : Function = null, externalAssetSrc : String = null ) : int
		{
			var result : int = -1;
			
			if ( listeners [ name ] == null ) return result;
			
			for ( var i : uint = 0; i < listeners [ name ].length; i++ )
			{
				if ( listeners [ name ] [ i ].onLoad === onLoad && listeners [ name ] [ i ].externalAssetSrc == externalAssetSrc )
				{
					result = i;
					break;
				}
			}
			
			return result;
		}
	}
}

